Code "LoadLevel"
//
	#include "BlackboardStatus" noemit
	
	#lib "GameMode"
	#lib "Lua"
//
{	
    // credit to Weezley for the original 'Fix Another Story Stages Sound' code
    if (GameMode.GetName() == "GameModeBattleRush")
	{
		// supported music customization options
		Lua.Call("LoadLevel", "actionchain_sound");
		Lua.Call("LoadLevel", "custom_sound");
		Lua.Call("LoadLevel", "w6_sound");		
		
		// unsupported music customization options
		Lua.Call("LoadLevel", "birthday_sound");
		Lua.Call("LoadLevel", "dragon01_sound");
		Lua.Call("LoadLevel", "extra_sound");
		Lua.Call("LoadLevel", "giant01_sound");
		Lua.Call("LoadLevel", "knight01_sound");
		Lua.Call("LoadLevel", "kodama_update_sound");
		Lua.Call("LoadLevel", "rifle01_sound");
		Lua.Call("LoadLevel", "riflebeast_sound");
		Lua.Call("LoadLevel", "the_end01_sound");	
		
		// Another Story object SFX
		Lua.Call("LoadLevel", "stage_islandobject_extra");
	}
}
Code "Mega Tower - UI text"
//
    #lib "Converse"
//
{		
	Converse.Redirect("menu_title_choice_battlerush_stage1", "[BR] Kronos");
	Converse.Redirect("menu_title_choice_battlerush_stage2", "[BR] Ares");
	Converse.Redirect("menu_title_choice_battlerush_stage3", "[BR] Chaos");
	Converse.Redirect("menu_title_choice_battlerush_stage4", "Mega-Tower Challenge");
	
	Converse.Redirect("menu_title_choice_battlerush", "Mega-Tower Challenge");
	
	// 'result_itemname_battlerush_stageX' would've been here, but Converse doesn't work with those lines, so i replaced them the traditional way
}

Code "Mega Tower - User modifiable parameters" by "ecksdii"
//
	#include "Reflection" noemit
	
	#import "Services"

	#lib "GameMode"
	#lib "HMM"
	#lib "INI"
	#lib "Lua"
	#lib "Reflection"
	
	#lib "BattleRushParameter"
	#lib "SoundCommonParameter"
//
{
    static bool isConfigInit = false;
	
	string isCustomEnabled = string.Empty;
	string IslandWeather = string.Empty;
	string OuranosBGM = string.Empty;
	string TimeOfDay = string.Empty;
	
	var name = GameMode.GetName();
	
	var BattleRushParameterInfo = Reflection.GetDataInfo<BattleRushParameter.Root>("battlerush");

	if (BattleRushParameterInfo.pData == null)
		return;
	
	var SoundCommonParameterInfo = Reflection.GetDataInfo<SoundCommonParameter.Root>("sound_common_param");

	if (SoundCommonParameterInfo.pData == null)
		return;

	var mod = HMM.GetModByID("B4198219");
	var modConfigIniPath = System.IO.Path.Combine(mod.Path, "config.ini");
	var ini = INI.Read(modConfigIniPath);	
	
	// while on the title screen and loading screen, reset config condition
	if (name == "GameModeTitle" || name == "GameModeUnknown")
    {
		isConfigInit = false;
	}
	else if (name == "GameModeBattleRush" && LevelInfo.GetCurrentStageID() == "w1b02")
    {
		if (!isConfigInit)
		{
			// reading ints or strings? no problem. reading bools? nahhhhh, i'm just not gonna read anything. why? just felt like it.
			isCustomEnabled = INI.Parse<string>(ini["Music_Custom"]["isCustomEnabled"], isCustomEnabled);
			
			if (isCustomEnabled == "Yes")
			{
				OuranosBGM = INI.Parse<string>(ini["Music_Custom"]["PhaseCue"], OuranosBGM);
			}
			else
			{
				OuranosBGM = INI.Parse<string>(ini["Settings"]["PhaseCue"], OuranosBGM);
			}					
			TimeOfDay = INI.Parse<string>(ini["Settings"]["PhaseTime"], TimeOfDay);
			
			switch(TimeOfDay)
			{
				case "Sunrise":
				{
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].hour, 4);
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].minute, 30);
					break;
				}		
				case "Day":
				{
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].hour, 8);
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].minute, 00);
					break;
				}		
				case "Sunset":
				{
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].hour, 20);
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].minute, 15);
					break;
				}		
				case "Night":
				{
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].hour, 0);
					RFL_SET_PARAM(BattleRushParameterInfo, stages[3].minute, 00);
					break;
				}
			}			

			RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].fadeInTime, 0.0f);
			RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].fadeOutTime, 2.5f);
			RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].cueName, OuranosBGM);	
			
			isConfigInit = true;
		}	
		else
		{
			IslandWeather = INI.Parse<string>(ini["Settings"]["PhaseWeather"], IslandWeather);		
			Lua.Call("ChangeWeather", 0, IslandWeather);
		}
    }
	else
	{
		// restore original sound cue and FadeIn/Out times
		RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].cueName, "bgm_miniboss_charger");
		RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].fadeInTime, 2.0f);
		RFL_SET_PARAM(SoundCommonParameterInfo, changeBgmInfo[2].fadeOutTime, 0.0f);
	}
}

Patch "Use global time for all respawns" by "angryzor"
{
	WriteProtected<uint>(0x1402173D5, 1);
}

Patch "Always enable JumpSelector UI" by "angryzor"
{
	WriteNop(0x140A341A2, 10);
}

Patch "HMM Code Check (modified)" by "ZEROES_07"
//
    #lib "HMM"
    using System.Windows.Forms;
    #load "System.Windows.Forms.dll"
//
{
    if (HMM.GetCodes().Contains("Gameplay/Open Zone/Objects/Disable Traversal Objects in Chests")) 
	{
        MessageBox.Show("Disable the code \"Gameplay/Open Zone/Objects/Disable Traversal Objects in Chests\" in order to play 'Mega-Tower Challenge'.", "Code Conflict", MessageBoxButtons.OK);
        System.Environment.Exit(0);
    }    
    else if (HMM.GetCodes().Contains("UI/Messages/Disable Tutorial Messages")) 
	{
        MessageBox.Show("Disable the code \"UI/Messages/Disable Tutorial Messages\" in order to play 'Mega-Tower Challenge'.", "Code Conflict", MessageBoxButtons.OK);
        System.Environment.Exit(0);
    }    
	else if (HMM.GetCodes().Contains("Fixes/Gameplay/Fix Damage from Jumping into Enemies")) 
	{
        MessageBox.Show("The code \"Fixes/Gameplay/Fix Damage from Jumping into Enemies\" can cause issues in the Final Horizon portion of 'Mega-Tower Challenge'.\n\nIt's recommended to disable this code before playing this mod.", "Code Conflict", MessageBoxButtons.OK);
    }
}

Library "SoundCommonParameter"
{
    #load "System.Numerics.dll"

    using System.Numerics;
    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Explicit, Size = 0x0C)]
    public struct BGMVolumeChangeInfo
    {
        [FieldOffset(0x00)] public float value;
        [FieldOffset(0x04)] public float delayTime;
        [FieldOffset(0x08)] public float fadeTime;
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x54)]
    public struct BGMVolumeChangeInfoList
    {
        [FieldOffset(0x00)] public BGMVolumeChangeInfo playerStop;
        [FieldOffset(0x0C)] public BGMVolumeChangeInfo playerWalk;
        [FieldOffset(0x18)] public BGMVolumeChangeInfo playerRun;
        [FieldOffset(0x24)] public BGMVolumeChangeInfo fishingHitIn;
        [FieldOffset(0x30)] public BGMVolumeChangeInfo fishingHitOut;
        [FieldOffset(0x3C)] public BGMVolumeChangeInfo musicBoxIn;
        [FieldOffset(0x48)] public BGMVolumeChangeInfo musicBoxOut;
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x08)]
    public struct AisacChangeInfo
    {
        [FieldOffset(0x00)] public float value;
        [FieldOffset(0x04)] public float fadeTime;
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x28)]
    public struct AisacChangeInfoList
    {
        [FieldOffset(0x00)] public AisacChangeInfo sunny;
        [FieldOffset(0x08)] public AisacChangeInfo cloudy;
        [FieldOffset(0x10)] public AisacChangeInfo rainy;
        [FieldOffset(0x18)] public AisacChangeInfo beginUnderWater;
        [FieldOffset(0x20)] public AisacChangeInfo finishUnderWater;
    }

    [StructLayout(LayoutKind.Explicit, Size = 8)]
    public struct UnmanagedString
    {
        [FieldOffset(0)] public long pValue;

        public string Value
        {
            get
            {
                if (pValue == 0)
                    return string.Empty;

                return Marshal.PtrToStringAnsi((nint)pValue);
            }

            set => pValue = (long)Marshal.StringToHGlobalAnsi(value);
        }

        public UnmanagedString(string in_value)
        {
            Value = in_value;
        }

        public static implicit operator UnmanagedString(string in_value)
        {
            return new UnmanagedString(in_value);
        }

        public static bool operator ==(UnmanagedString in_left, string in_right)
        {
            return in_left.Value == in_right;
        }

        public static bool operator !=(UnmanagedString in_left, string in_right)
        {
            return !(in_left == in_right);
        }

        public override bool Equals(object in_obj)
        {
            if (in_obj is string str)
                return Value == str;

            return base.Equals(in_obj);
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        public override string ToString()
        {
            return Value;
        }
    }

    public enum Priority : sbyte
    {
        PRIORITY_LOW = 0,
        PRIORITY_DEFAULT = 1,
        PRIORITY_HIGH = 2,
        PRIORITY_SUPER_HIGH = 3,
        NUM_PRIORITY = 4,
        PRIORITY_INVALID = -1
    }

    public enum InterruptType : sbyte
    {
        INTERRUPT_TYPE_STOP = 0,
        INTERRUPT_TYPE_PAUSE = 1,
        INTERRUPT_TYPE_OVERLAP = 2,
        NUM_INTERRUPT_TYPE = 3
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x40)]
    public struct BGMInfo
    {
        [FieldOffset(0x00)] public UnmanagedString cueName;
        [FieldOffset(0x10)] public int blockId;
        [FieldOffset(0x14)] public float fadeInTime;
        [FieldOffset(0x18)] public float fadeOutTime;
        [FieldOffset(0x1C)] public float delayTime;
        [FieldOffset(0x20)] public UnmanagedString aisacName;
        [FieldOffset(0x30)] public float aisacValue;
        [FieldOffset(0x34)] public float aisacFadeTime;
        [FieldOffset(0x38)] public bool isVolumeControl;
        [FieldOffset(0x39)] public bool isOverlapPlay;
        [FieldOffset(0x3A)] public Priority priority;
        [FieldOffset(0x3B)] public InterruptType interruptType;
    }

    public enum BGMTransitInfo_InterruptType : sbyte
    {
        BGMTransitInfo_InterruptType_INTERRUPT_TYPE_STOP = 0,
        BGMTransitInfo_InterruptType_INTERRUPT_TYPE_PAUSE = 1,
        BGMTransitInfo_InterruptType_INTERRUPT_TYPE_OVERLAP = 2,
        BGMTransitInfo_InterruptType_NUM_INTERRUPT_TYPE = 3
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x30)]
    public struct BGMTransitInfo
    {
        [FieldOffset(0x00)] public UnmanagedString tagName;
        [FieldOffset(0x10)] public int beforeId;
        [FieldOffset(0x14)] public int beforeIdMax;
        [FieldOffset(0x18)] public int afterId;
        [FieldOffset(0x1C)] public BGMTransitInfo_InterruptType type;
        [FieldOffset(0x20)] public float fadeOutTime;
        [FieldOffset(0x24)] public float fadeInTime;
        [FieldOffset(0x28)] public float delayTime;
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x3890)]
    public struct Root
    {
        [FieldOffset(0x00)] public float pauseFadeOutTime;
        [FieldOffset(0x04)] public float pauseFadeInTime;
        [FieldOffset(0x08)] public float enterCyberFadeOutTime;
        [FieldOffset(0x0C)] public float leaveCyberResultFadeOutTime;
        [FieldOffset(0x10)] public BGMVolumeChangeInfoList bgmVolumeInfo;
        [FieldOffset(0x64)] public AisacChangeInfoList aisacInfo;
        [FieldOffset(0x90)] public unsafe fixed byte /* BGMInfo[128] */ _changeBgmInfo[8192];

        public unsafe BGMInfo* changeBgmInfo
        {
            get
            {
                fixed (byte* p_changeBgmInfo = _changeBgmInfo)
                    return (BGMInfo*)p_changeBgmInfo;
            }
        }

        [FieldOffset(0x2090)] public unsafe fixed byte /* BGMTransitInfo[128] */ _transitBgmInfo[6144];

        public unsafe BGMTransitInfo* transitBgmInfo
        {
            get
            {
                fixed (byte* p_transitBgmInfo = _transitBgmInfo)
                    return (BGMTransitInfo*)p_transitBgmInfo;
            }
        }
    }
}

Library "BattleRushParameter"
{
    #load "System.Numerics.dll"

    using System.Numerics;
    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Explicit, Size = 0x20)]
    public struct BattleRushPhaseRankParameter
    {
        [FieldOffset(0x00)] public unsafe fixed uint rankTime[4];
        [FieldOffset(0x10)] public unsafe fixed uint rankTimeAll[4];
    }

    public enum BgmType : sbyte
    {
        INVALID = -1,
        ENEMY_BATTLE = 0,
        FORCE_BATTLE_FIELD = 1,
        MINIBOSS_CHARGER_BATTLE = 2,
        MINIBOSS_DARUMA_BATTLE = 3,
        MINIBOSS_SPIDER_BATTLE = 4,
        MINIBOSS_BLADE_BATTLE = 5,
        MINIBOSS_FLYER_BATTLE = 6,
        MINIBOSS_TRACKER_BATTLE = 7,
        MINIBOSS_ASHURA_BATTLE = 8,
        MINIBOSS_SKIER_BATTLE = 9,
        MINIBOSS_SUMO_BATTLE = 10,
        MINIBOSS_TYRANT_BATTLE = 11,
        MINIBOSS_STRIDER_BATTLE = 12,
        MINIBOSS_WARSHIP_BATTLE = 13,
        ENEMY_AQUABALL = 14,
        TUTORIAL_BLADE_BATTLE = 15
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x1FC)]
    public struct BattleRushStageParameter
    {
        [FieldOffset(0x00)] public uint numPhases;
        [FieldOffset(0x04)] public unsafe fixed float phaseLimitTime[11];
        [FieldOffset(0x30)] public unsafe fixed float phaseClearedTime[11];
        [FieldOffset(0x5C)] public unsafe fixed float phaseResultTime[11];
        [FieldOffset(0x88)] public unsafe fixed byte /* BattleRushPhaseRankParameter[11] */ _phaseRank[352];

        public unsafe BattleRushPhaseRankParameter* phaseRank
        {
            get
            {
                fixed (byte* p_phaseRank = _phaseRank)
                    return (BattleRushPhaseRankParameter*)p_phaseRank;
            }
        }

        [FieldOffset(0x1E8)] public unsafe fixed byte /* BgmType[11] */ _phaseBgmType[11];

        public unsafe BgmType* phaseBgmType
        {
            get
            {
                fixed (byte* p_phaseBgmType = _phaseBgmType)
                    return (BgmType*)p_phaseBgmType;
            }
        }

        [FieldOffset(0x1F4)] public uint hour;
        [FieldOffset(0x1F8)] public uint minute;
    }

    [StructLayout(LayoutKind.Explicit, Size = 0x7F4)]
    public struct Root
    {
        [FieldOffset(0x00)] public unsafe fixed byte /* BattleRushStageParameter[4] */ _stages[2032];

        public unsafe BattleRushStageParameter* stages
        {
            get
            {
                fixed (byte* p_stages = _stages)
                    return (BattleRushStageParameter*)p_stages;
            }
        }

        [FieldOffset(0x7F0)] public float viewPaseResultTime;
    }

}